###########################################################################
# $Id: AutoIndex.p,v 1.4 2008-02-08 15:02:15 misha Exp $
###########################################################################

@CLASS
AutoIndex



###########################################################################
@auto[]
$sClassName[AutoIndex]
#end @auto[]



###########################################################################
# $.tExclude	- list of excluded files (named table with 1 column)
# $.bAutoCreatePreviews
# $.oImg		- object which used for create previews
# $.iWidth		- max width of preview
# $.iHeight		- max height of previews
# $.bRemoveUnusedPreviews

# $.hOrder

# $.bIconsAreLinks
# $.bSuppressPreview
# $.bSuppressIcon
# $.bSuppressDescription
# $.bSuppressLastModified
# $.bSuppressSize

# $.bSuppressSorting

@create[sDir;hParam][tPiece;tCol]
$hParam[^hash::create[$hParam]]

$self.sDir[^sDir.trim[end;/]]

^if(!def $self.sDir){
	^self._throw[^$sDir must be specified.]
}

^if(!-d $self.sDir){
	^self._throw[Directory '$sDir' doesn't exist.]
}

$oImg[$hParam.oImg]

$bAutoCreatePreviews(^hParam.bAutoCreatePreviews.bool(false))
^if($bAutoCreatePreviews && !def $oImg){
	^self._throw[^$.oImg must be specified for creating previews automatically.]
}

$bRemoveUnusedPreviews(^hParam.bRemoveUnusedPreviews.bool(false))

$bIconsAreLinks(^hParam.bIconsAreLinks.bool(true))

$hSuppress[
	$.preview(^hParam.bSuppressPreview.bool(false))
	$.icon(^hParam.bSuppressIcon.bool(false))
	$.modified(^hParam.bSuppressLastModified.bool(false))
	$.size(^hParam.bSuppressSize.bool(false))
	$.description(^hParam.bSuppressDescription.bool(false))
]

$bSuppressSorting(^hParam.bSuppressSorting.bool(false))

$hOption[
	$.hPreview[
		$.sPrefix[_]
		$.iWidth(^hParam.iWidth.int(120))
		$.iHeight(^hParam.iHeight.int(120))
	]
	$.hImage[
		$.sExtMask[\.(?i:jpe?g|gif|png|jpe)^$]
	]

	$.hOrder[
		$.sField[^if(def $hParam.hOrder.sField){$hParam.hOrder.sField}{N}]
		$.sDirection[^if(def $hParam.hOrder.sDirection){$hParam.hOrder.sDirection}{asc}]
	]
	
	$.sDescriptionFileName[descript.ion]
	
	^rem{ *** default excludes *** }
	$.hExclude[
		$.[.htaccess](1)
		$.[.htpasswd](1)
		$.[auto.p](1)
		$.[index.html](1)
		$.[descript.ion](1)
	]
]

^if($hParam.tExclude is "table"){
	$tCol[^hParam.tExclude.columns[]]
	^hParam.tExclude.menu{
		$hOption.hExclude.[$hParam.tExclude.[$tCol.column]](1)
	}
}

$hCache[
	$.hFile[^hash::create[]]
	$.hDescription[^hash::create[]]
]

$hMonth[
	$.1[Jan]
	$.2[Feb]
	$.3[Mar]
	$.4[Apr]
	$.5[May]
	$.6[Jun]
	$.7[Jul]
	$.8[Aug]
	$.9[Sep]
	$.10[Oct]
	$.11[Nov]
	$.12[Dec]
]
#end @create[]



###########################################################################
@exec[hParam]
^self.printBody{
	<table class="autoindex">
	<tr>
		^if(!$hSuppress.preview){
			<th><br /></th>
		}
		^if(!$hSuppress.icon){
			<th><br /></th>
		}
		<th>^self.printOrderLink[N;Name]</th>
		^if(!$hSuppress.modified){
			<th>^self.printOrderLink[M;Last Modified]</th>
		}
		^if(!$hSuppress.size){
			<th>^self.printOrderLink[S;Size]</th>
		}
		^if(!$hSuppress.description){
			<th>Description</td>
		}
	</tr>

	^self.printSeparator(6)

	<tr>
		^if(!$hSuppress.preview){<td><br/></td>}
		^if(!$hSuppress.icon){
			<td><a href="../"><img src="/icons/back.gif" width="20" border="0"/></a></td>
		}
		<td class="name"><a href="../">Parent Directory</a></td>
		^if(!$hSuppress.modified){<td><br /></td>}
		^if(!$hSuppress.size){<td><br /></td>}
		^if(!$hSuppress.description){<td><br /></td>}
	</tr>
	
	^self.printIndex[]
	
	^self.printSeparator(6)

	</table>

	<address>Directory index generated by AutoIndex class written by Misha v.3.</address>
}
#end @exec[]



###########################################################################
@printOrderLink[sField;sTitle][sOrderField;sDirection]
^if($bSuppressSorting){
	$result[$sTitle]
}{
	$sOrderField[^if(def $form:C){$form:C}{N}]
	$sDirection[^if(def $form:O){$form:O}{$hOption.hOrder.sDirection}]
	$result[<a href="./?C=$sField&O=^if($sOrderField eq $sField && $sDirection ne "desc"){desc}{asc}">$sTitle</a>]
}
#end @printOrderLink[]



###########################################################################
@printBody[jBody][sTitle]
$sTitle[Index of $sDir]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
	<head>
		<title>$sTitle</title>
		<style><!--
			body {
				background-color: white^;
			}
			table.autoindex td {
				font-family: monospace^;
				padding: 0.1em;
				font-size: 80%^;
				vertical-align: top^;
			}
			table.autoindex td.preview {
				text-align: center^;
			}
			table.autoindex td.name {
				white-space: nowrap^;
			}
			table.autoindex td.modified, table.autoindex td.size {
				text-align: right^;
				white-space: nowrap^;
				padding-right: 1em^;
			}
			div.dimensions {
				font-size: 90%^;
				color: gray^;
			}
		//-->
		</style>
	</head>
	<body>
		<h1>$sTitle</h1>
		$jBody
	</body>
</html>
#end @printBody[]



###########################################################################
@printIndex[]
^self.loadDirectoryData[]

^self.printList[dir]
^self.printList[file]
#end @printIndex[]



###########################################################################
@printSeparator[iColumns][bValue]
^hSuppress.foreach[;bValue]{^if($bValue){^iColumns.dec(1)}}
<tr><td colspan="$iColumns"><hr /></td></tr>
#end @printSeparator[]



###########################################################################
@loadDirectoryData[][tFile;f;tLine;tDesc;sName]
$tFile[^file:list[$sDir]]
$tFile[^tFile.select(!$hOption.hExclude.[$tFile.name])]
^tFile.menu{
	^self.putItem[$tFile.name]
}

^if(!$hSuppress.description && -f "$sDir/$hOption.sDescriptionFileName"){
	^rem{ *** load descriptions from file if needed *** }
	$f[^file::load[text;$sDir/$hOption.sDescriptionFileName]]
	$tLine[^f.text.match[^^(.+)^$][mg]]
	^tLine.menu{
		^if(^tLine.1.pos["] == 0){
			$tDesc[^tLine.1.match[^^"([^^"]+)"\s+(.+)^$]]
		}{
			$tDesc[^tLine.1.match[^^(\S+)\s+(.+)^$]]
		}
		$hCache.hDescription.[$tDesc.1][$tDesc.2]
	}
}

^if($bRemoveUnusedPreviews){
	^hCache.hFile.foreach[sName;]{
		^if(
			$hCache.hFile.[$sName].sType eq "preview"
			&& !$hCache.hFile.[^sName.match[^^$hOption.hPreview.sPrefix][]{}]
		){
			^file:delete[$sDir/$sName]
			^self.deleteItem[$sName]
		}
	}
}
^memory:compact[]
$result[]
#end @loadDirectoryData[]



###########################################################################
@putItem[sName][sFileName;fStat;fImg;sType;sExt]
$sFileName[$sDir/$sName]
$sType[^if(-f $sFileName){^if(^self.isPreview[$sName]){preview}{file}}{dir}]
$sExt[^file:justext[$sName]]

$hCache.hFile.[$sName][
	$.sType[$sType]
	$.sName[$sName]
	$.sExt[^sExt.lower[]]

	$fStat[^file::stat[$sFileName]]
	$.dtModified[$fStat.mdate]

	^if($sType ne "dir"){
		$.iSize($fStat.size)
		^if($sType eq "preview" || ^self.isImage[$sName]){
			$.bImage(true)
			^try{
				$fImg[^image::measure[$sFileName]]
				$.iWidth($fImg.width)
				$.iHeight($fImg.height)
			}{
				$exception.handled(true)
			}
		}
	}
]
$result[]
#end @putItem[]



###########################################################################
@getItem[sName]
$result[$hCache.hFile.$sName]
#end @getItem[]



###########################################################################
@deleteItem[sName]
^hCache.hFile.delete[$sName]
$result[]
#end @deleteItem[]


###########################################################################
@getDescription[sName]
$result[$hCache.hDescription.$sName]
#end @getDescription[]



###########################################################################
@printList[sType][tList;sName]
$tList[^table::create{sName
^hCache.hFile.foreach[sName;]{^if($hCache.hFile.[$sName].sType eq $sType){$sName
}}
}]

^self.orderList[$tList]

^tList.menu{
	^self.printItem[^self.getItem[$tList.sName]]
}
#end @printList[]



###########################################################################
@printItem[hItem][sName;sIcon;sPreview;sPreviewName;hPreview]
^switch[$hItem.sType]{
	^case[dir]{
		$sName[$hItem.sName/]
	}
	^case[file]{
		$sName[$hItem.sName]

		^if(!$hSuppress.preview){
			$sPreviewName[^self.makePreviewName[$sName]]
			$hPreview[^self.getItem[$sPreviewName]]

			^if(
				$hItem.bImage
				&& $bAutoCreatePreviews
				&& (
					!def $hPreview
					|| $hPreview.dtModified < $hItem.dtModified	# image newer then preview
				)
				&& !^self.generatePreview[$sName]
			){
				$hPreview[^self.getItem[$sPreviewName]]
			}

			^if($hPreview && $hPreview.bImage){
				$sPreview[<a href="$sName"><img src="$hPreview.sName" width="$hPreview.iWidth" height="$hPreview.iHeight" border="0" /></a>]
			}
		}

	}
}

<tr>
	^if(!$hSuppress.preview){
		<td class="preview">^if(def $sPreview){$sPreview}{<br />}</td>
	}
	^if(!$hSuppress.icon){
		$sIcon[^self.getItemIcon[$sName;$hItem]]
		<td>^if(def $sIcon){$sIcon}{<br />}</td>
	}
	<td class="name"><a href="$sName">$sName</a>^if($hItem.bImage){<div class="dimensions">${hItem.iWidth}x$hItem.iHeight</div>}</td>
	^if(!$hSuppress.modified){
		<td class="modified">^self.printDate[$hItem.dtModified]</td>
	}
	^if(!$hSuppress.size){
		<td class="size">^self.printFileSize($hItem.iSize)</td>
	}
	^if(!$hSuppress.description){
		<td>^self.getDescription[$sName]</td>
	}
</tr>
#end @printItem[]



###########################################################################
@getItemIcon[sHREF;hItem][sFileName]
^switch[$hItem.sType]{
	^case[dir]{
		$sFileName[dir.gif]
	}
	^case[file]{
		^if($hItem.bImage){
			$sFileName[image2.gif]
		}{
			$sFileName[^switch[$hItem.sExt]{
				^case[tar]{tar.gif}
				^case[zip;rar;gz;tgz]{compressed.gif}
				^case[dll;exe]{binary.gif}
				^case[mov;avi;mp4;mpg;mpeg]{movie.gif}
				^case[txt;htm;html]{text.gif}
				^case[DEFAULT]{unknown.gif}
			}]
		}
	}
}
^if(def $sFileName){
	$result[<img src="/icons/$sFileName" width="20" border="0" />]
	^if($bIconsAreLinks && def $sHREF){
		$result[<a href="$sHREF">$result</a>]
	}
}{
	$result[]
}
#end @getItemIcon[]



###########################################################################
@isImage[sName]
$result(^sName.match[^^[^^${hOption.hPreview.sPrefix}].+$hOption.hImage.sExtMask])
#end @isImage[]



###########################################################################
@isPreview[sName]
$result(^sName.match[^^${hOption.hPreview.sPrefix}.+$hOption.hImage.sExtMask])
#end @isPreview[]



###########################################################################
@makePreviewName[sName]
$result[${hOption.hPreview.sPrefix}$sName]
#end @makePreviewName[]



###########################################################################
@generatePreview[sName][sPreviewName]
$sPreviewName[^self.makePreviewName[$sName]]
$result(^oImg.resize[$sDir/$sName;$sDir/$sPreviewName;$hOption.hPreview.iWidth;$hOption.hPreview.iHeight;
	$.sResizeType[decr]
])
^if(!$result){
	^self.putItem[$sPreviewName]
}
#end @generatePreview[]



###########################################################################
@orderList[tList][sO;sField;sDirection]
^if(!$bSuppressSorting){
	$sO[$form:O]
	$sField[$form:C]
}
^if(!def $sField){
	$sField[$hOption.hOrder.sField]
}
^if(!def $sO){
	$sO[$hOption.hOrder.sDirection]
}

$sDirection[^if(^sO.lower[] eq "desc"){desc}{asc}]

^switch[$sField]{
	^case[M]{
		^tList.sort($hCache.hFile.[$tList.sName].dtModified)[$sDirection]
	}
	^case[S]{
		^tList.sort($hCache.hFile.[$tList.sName].iSize)[$sDirection]
	}
	^case[N]{
		^tList.sort{^tList.sName.lower[]}[$sDirection]
	}
}
$result[]
#end @orderList[]



###########################################################################
@printFileSize[iSize]
^if(def $iSize){
	^if($iSize < 1024){
		$result[$iSize]
	}{
		^if($iSize < 1048576){
			$result[^eval($iSize/1024)[%.1f]K]
		}{
			$result[^eval($iSize/1048576)[%.1f]M]
		}
	}
	$result[^result.match[\.0+][]{}]
}{
	$result[]
}
#end @printFileSize[]



###########################################################################
@printDate[dt]
$result[^dt.day.format[%02d]-${hMonth.[$dt.month]}-$dt.year ^dt.hour.format[%02d]:^dt.minute.format[%02d]]
#end @printDate[]



###########################################################################
@_throw[sSource;sComment]
^throw[$sClassName;$sSource;$sComment]



